﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using ShadowCommando.Game.Elements;
using ShadowCommando.Game.Utilites;
using ShadowCommando.Game.GameGraphics;
using ShadowCommando.Game.GameBoard;
using ShadowCommando.Game.Elements.Obstacle;
using ShadowCommando.Game.Utilites.Map;
using ShadowCommando.Game.Elements.Bonus;
using ShadowCommando.Game.GameSounds;
using ShadowCommando.Game.GameGraphics.Resources;
namespace ShadowCommando.Game
{
    /// <summary>
    /// Delegacja do obsługi gier
    /// </summary>
    public delegate void GameDelegate();
    /// <summary>
    /// Kontrolka reprezentująca całą rozgrywkę i jest odpowiedzialna za jej obsługe
    /// </summary>
    public partial class Game : UserControl
    {

        /// <summary>
        /// Konstruktor
        /// </summary>
        public Game()
        {
            InitializeComponent();
            Configurations();
            gameSound=new GameSound();
            
        }
        private void Configurations()
        {
            DoubleBuffered = true;
            Dock = DockStyle.Fill;
            BackColor = Color.Green;
            font = GameSettings.Default.MessageFont;
            fontColor = GameSettings.Default.MessageColor;
        }
        /// <summary>
        /// Start misjii
        /// </summary>
        public void Start()
        {
            InitgameGraphic();
            InitBoard();
            InitDrawingList();
            InitPressedKey();

            InitRefreshTimerAction();
            InitBulletMoveAction();
            InitEnemyMoveAction();
            InitDeadTimerAction();
            InitBombLoadAction();
            InitExplodeBombAction();
            InitMissionCompletedAction();
            InitEnemyAtackAction();
            InitEnemyWakeUpAction();
            InitDriodDirectionAction();
            InitRemoveDeadElements();
            InitEnemyFind();
            InitAnimationAction();

            InitObstacle();
            InitBulletList();
            InitAimList();
            InitBombList();
            InitEnemy();
            InitHero();
            InitDeads();
            InitBonus();

            InitItems();
            InitWalll();

            LoadMission(mapLevel);
            InitTimer();
            started = true;
            DisplayScreen();
        }
        #region Items

        private event Action<int> savePointEvent;
        private event GameDelegate esacepeButtonEvent;
        private LevelDifficulty difficulty = LevelDifficulty.JediMaster;
        private MapSettings mapSettings;
        private Font font;
        private Color fontColor;
        
        private GameSound gameSound;

        private int oldPts;
        private int pts;
        private int mapLevel = 1;
        private int loadRecWidth = GameSettings.Default.LoadRecWidth;
        private int loadRecHeight = GameSettings.Default.LoadRecHeight;
        private int mapWidth;
        private int mapHeight;
        private int step = GameSettings.Default.Step;
        private int bombCount;
        private int findCount=0;

        private bool IsBombLoading = false;
        private bool isMessagePressed = false;
        private bool started = false;
        private bool stoped = false;

        private Rectangle loadRectangle;
        private Rectangle messageRectangle;
        private string message;
     
        private event TimerDelegate refreshBoard;
        
        private GameGraphic gameGraphic;
        private Hero hero;
       

        private List<Bullet> bombList;
        private List<Keys> pressedKeys;
        private List<Bullet> bulletList;
        private List<Element> drawingList;
        private List<Obstacle> obstacleList;
        private List<Enemy> enemyList;
        private List<Element> deadList;
        private List<Obstacle> wallList;
        private List<Obstacle> aimList;
        private List<Bonus> bonusList;
        private List<DeadElement> deadElementList;
        private List<Element> animationList;

        private TimerAction deadTimerAction;
        private TimerAction RefreshTimerAction;
        private TimerAction bulletMoveAction;
        private TimerAction enemyMoveAction;
        private TimerAction enemyFindAction;
        private TimerAction bombLoadAction;
        private TimerAction explodeBombAction;
        private TimerAction missionCompletedAction;
        private TimerAction enemyAtackAction;
        private TimerAction enemyWakeUpAction;
        private TimerAction droidDirectonAction;
        private TimerAction animationAction;
        private TimerAction removeDeadElementsAction;

        private Timer timer;
        private Board board;
        #endregion

        #region Initalization

        private void InitItems()
        {

            loadRectangle = new Rectangle();
            loadRectangle.Width = 0;
            loadRectangle.Height = loadRecHeight;
            loadRectangle.X = Width / 2- loadRecWidth/4;
            loadRectangle.Y = Height / 2 - loadRectangle.Height / 2;
            
            messageRectangle=new Rectangle();
            font=new Font("Arial",20);
            
        }
        private void InitgameGraphic()
        {
            gameGraphic = new GameGraphic();
            mapWidth = 4*Width;
            mapHeight = 4*Height;
        }
        private void InitBoard()
        {
            //Dodac do resource
            board=new Board(mapWidth,mapHeight,step);
        }
        private void InitDriodDirectionAction()
        {
            droidDirectonAction=new TimerAction();
            droidDirectonAction.ActualTimerCount = 0;
            droidDirectonAction.TimerCount = 300;
            droidDirectonAction.Action += DroidDirection;
        }
        private void InitBombLoadAction()
        {
            bombLoadAction=new TimerAction();
            bombLoadAction.ActualTimerCount = 0;
            bombLoadAction.TimerCount = 5;
            bombLoadAction.Action += BombLoad;
        }
        private void InitDeadTimerAction()
        {
            deadTimerAction=new TimerAction();
            deadTimerAction.ActualTimerCount = 0;
            deadTimerAction.TimerCount = 10;
            deadTimerAction.Action += DeleteDeadElement;
        }
        private void InitEnemyMoveAction()
        {
            enemyMoveAction = new TimerAction();
            enemyMoveAction.ActualTimerCount = 0;
            enemyMoveAction.TimerCount = 2;
            enemyMoveAction.Action += EnemyMove;
        }
        private void InitEnemyWakeUpAction()
        {
            enemyWakeUpAction=new TimerAction();
            enemyWakeUpAction.ActualTimerCount = 0;
            enemyWakeUpAction.TimerCount = 50;
            enemyWakeUpAction.Action += EnemyWakeUp;
        }
        private void InitEnemyFind()
        {
            enemyFindAction=new TimerAction();
            enemyFindAction.ActualTimerCount = 0;
            enemyFindAction.TimerCount = 100;
            enemyFindAction.Action += EnemyFind;
        }
        private void InitBonus()
        {
            bonusList=new List<Bonus>();
        }
        private void InitDeads()
        {
            deadList=new List<Element>();
        }
        private void InitRefreshTimerAction()
        {
            RefreshTimerAction = new TimerAction();
            RefreshTimerAction.ActualTimerCount = 0;
            RefreshTimerAction.TimerCount = 1;
            RefreshTimerAction.Action += RefreshScreen;
        }
        private void InitBulletMoveAction()
        {
            bulletMoveAction = new TimerAction();
            bulletMoveAction.ActualTimerCount = 0;
            bulletMoveAction.TimerCount = 1;
            bulletMoveAction.Action += BulletMove;
        }
        private void InitTimer()
        {
            timer = new Timer();
            timer.Interval = 20;
            timer.Tick += new EventHandler(timer_Tick);
           
        }
        private void InitExplodeBombAction()
        {
            explodeBombAction = new TimerAction();
            explodeBombAction.ActualTimerCount = 0;
            explodeBombAction.TimerCount = 20;
            explodeBombAction.Action += ExplodeBomb;
        }
        private void InitMissionCompletedAction()
        {
            missionCompletedAction = new TimerAction();
            missionCompletedAction.ActualTimerCount = 0;
            missionCompletedAction.TimerCount = 100;
            missionCompletedAction.Action += CheckAimMission;
        }
        private void InitEnemyAtackAction()
        {
            enemyAtackAction=new TimerAction();
            enemyAtackAction.ActualTimerCount = 0;
            enemyAtackAction.TimerCount = 30;
            enemyAtackAction.Action += EnemyAtack;
        }
        private void InitRemoveDeadElements()
        {
            deadElementList=new List<DeadElement>();
            removeDeadElementsAction=new TimerAction();
            removeDeadElementsAction.ActualTimerCount = 0;
            removeDeadElementsAction.TimerCount = 10;
            removeDeadElementsAction.Action += RemoveDeadElements;
        }
        private void InitAnimationAction()
        {
         animationList=new List<Element>();   
         animationAction=new TimerAction();
            animationAction.ActualTimerCount = 0;
            animationAction.TimerCount = 5;
            animationAction.Action += MakeAnimation;

        }
        private void InitBombList()
        {
            bombList = new List<Bullet>();
        }
        private void InitPressedKey()
        {
            pressedKeys = new List<Keys>();
        }
        private void InitDrawingList()
        {
            drawingList = new List<Element>();
        }
        private void InitBulletList()
        {
            bulletList = new List<Bullet>();

        }
        private void InitObstacle()
        {
            obstacleList = new List<Obstacle>();
       
    }
        private void InitHero()
        {
            hero = new Hero(step,difficulty,HeroId.Test);
            hero.FakeLocation = new Point(Width / 2, Height / 2);
            hero.RealLocation = new Point(Width / 2, Height / 2);
            
            gameGraphic.SetElementSize(hero);
           
            drawingList.Add(hero);
            DisplayScreen();
        }
        private void InitAimList()
        {
            aimList = new List<Obstacle>();
        }

        private void InitEnemy()
        {
            enemyList=new List<Enemy>();
     
        }
        private void InitWalll()
        {

            wallList=new List<Obstacle>();
            Obstacle wall=new Obstacle(step,difficulty,ObstacleId.Wall);
            gameGraphic.SetElementSize(wall);
            int wallW = wall.Width;
            int wallH = wall.Height;
            int w = mapWidth/wall.Width;
            int h = mapHeight/wall.Height;
            for(int i=0;i<h;i++)
            {
               wall=new Obstacle(step,difficulty,ObstacleId.Wall);
               gameGraphic.SetElementSize(wall);
                wall.FakeX = 0;
                wall.FakeY = i*wallH;
                wallList.Add(wall);
                board.SetElement(wall);
                board.SetField(wall, false);
                wall = new Obstacle(step,difficulty, ObstacleId.Wall);
                gameGraphic.SetElementSize(wall);
                wall.FakeX = (w-1)*wallW;
                wall.FakeY = i * wallH;

                board.SetField(wall, false);
                wallList.Add(wall);
            }
            for (int i = 0; i < w; i++)
            {
                wall = new Obstacle(step,difficulty, ObstacleId.Wall);
                gameGraphic.SetElementSize(wall);
                wall.FakeX = i*wallW;
                wall.FakeY = 0;
                wallList.Add(wall);
                wall = new Obstacle(step,difficulty, ObstacleId.Wall);
                gameGraphic.SetElementSize(wall);
                wall.FakeX = i * wallW;
                wall.FakeY = (h-1) * wallH;
                wallList.Add(wall);
            }
        }
        /// <summary>
        /// Ładuje misję
        /// </summary>
        /// <param name="n">Nr levelu</param>
        private void LoadMission(int n)
        {
            Color bColor = Color.White;
            stoped = false;
            mapSettings = new MapSettings(n, mapWidth, mapHeight, step, difficulty);
            mapSettings.CreateMap(hero, ref enemyList, ref bonusList, ref obstacleList, ref aimList,ref bColor,ref message);
            BackColor=bColor;
            foreach (Obstacle o in obstacleList)
            {
                board.SetElement(o);
                board.SetField(o, !o.Movable);
            }
            drawingList.Clear();
            drawingList.Add(hero);
            DisplayScreen();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Posiadane pts po zakończeniu misji
        /// </summary>
        public int OldPts
        {
            get { return oldPts; }
        }
        /// <summary>
        /// Wyłącza dźwięki
        /// </summary>
        public bool MuteSound
        {
            get { return gameSound.SoundMute; }
            set { gameSound.SoundMute = value; }
        }
        /// <summary>
        /// Zdarzenie obsługujące przycisk esc
        /// </summary>
        public event GameDelegate EscapeButtonEvent
        {
          add { esacepeButtonEvent = value; }
           remove { esacepeButtonEvent = value; }
        }
        /// <summary>
        /// Zapis gry
        /// </summary>
        public Action<int> SavePointEvent
        {
            get { return savePointEvent; }
            set { savePointEvent = value; }
        }
        /// <summary>
        /// Poziom trudności
        /// </summary>
       public LevelDifficulty LevelDifficulty
        {
            get { return difficulty; }
            set { difficulty = value; }
        }
        /// <summary>
        ///Odswiezanie wyswietlania stanu hp broni itp
        /// </summary>
        public event TimerDelegate RefreshBoard
        {
            add {  refreshBoard=value; }
            remove { refreshBoard = value; }
        }
        /// <summary>
        /// Liczba pocisków
        /// </summary>
        public int ActualBulletCount
        {
            get { return hero.ActualWeapon.BulletCount; }
        }
        /// <summary>
        /// Nazwa aktualnej broni
        /// </summary>
        public string ActualWeaponName
        {
            get
            {
                StringBuilder sb= new StringBuilder();
                sb.AppendLine(hero.ActualWeapon.BulletId.ToString() + " : " + hero.ActualWeapon.BulletCount.ToString() + "/" +
                              hero.ActualWeapon.MaxBullet.ToString());
                
                return sb.ToString();
            }
        }
        /// <summary>
        /// Nazwy wszystkich broni
        /// </summary>
        public string WeaponsName
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (Weapon weapon in hero.Weapons)
                {
                    if (weapon.BulletId != hero.ActualWeapon.BulletId)
                        sb.AppendLine(weapon.BulletId.ToString() + " : " + weapon.BulletCount + "/" + weapon.MaxBullet.ToString());
                }
                return sb.ToString();
               
            }
        }
        /// <summary>
        /// Obrazek dla Pts
        /// </summary>
        public Bitmap StarImage
        {
            get { return gameGraphic.MakeTransparent(OtherResource.StarImage); }
        }
        /// <summary>
        /// Obrazek dla Armor
        /// </summary>
        public Bitmap ShieldImage
        {
            get { return gameGraphic.MakeTransparent(OtherResource.ShieldImage); }
        }
        /// <summary>
        /// Obrazek dla HP
        /// </summary>
        public Bitmap HeartImage
        {
            get { return gameGraphic.MakeTransparent(OtherResource.HeartResource); }
        }
        /// <summary>
        /// Czy gra wystartowala
        /// </summary>
        public bool Started
        {
            get { return started; }
        }
        /// <summary>
        /// Punkty gracza
        /// </summary>
        public int Pts
        {
            get { return pts; }
        }
        /// <summary>
        /// Życie
        /// </summary>
        public int Life
        {
            get { return hero.Life; }
        }
        /// <summary>
        /// Pancerz
        /// </summary>
        public int Armor
        {
            get { return hero.Armor; }
        }
        /// <summary>
        /// Poziom gry
        /// </summary>
        public int MapLvl
        {
            get { return mapLevel; }
            set { mapLevel = value; }
        }
        #endregion

     

        #region MyEvents
        private void timer_Tick(object sender, EventArgs e)
        {
            if(stoped)
                return;
            bulletMoveAction.Tick();
            enemyFindAction.Tick();
            enemyMoveAction.Tick();
            deadTimerAction.Tick();
            enemyAtackAction.Tick();
            enemyWakeUpAction.Tick();
            explodeBombAction.Tick();
            droidDirectonAction.Tick();
            missionCompletedAction.Tick();
            removeDeadElementsAction.Tick();
            animationAction.Tick();
            if(IsBombLoading)
                bombLoadAction.Tick();
            RefreshTimerAction.Tick();
            
        }
        #endregion

        #region OverrideEvents
        /// <summary>
        /// Obsługa wciskania klawisza
        /// </summary>
        /// <param name="e">Argument przycisku</param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space)
            {
                double min = double.MaxValue;
                Element el=null;
                for (int i = 0; i < aimList.Count;i++ )
                {
                    if (Distance(aimList[i].FakeLocation, hero.FakeLocation) < min)
                    {
                        el = aimList[i];
                        min = Distance(aimList[i].FakeLocation, hero.FakeLocation);
                    }
                }

                    if (el!=null)
                        MessageBox.Show(hero.FakeLocation.ToString() + " " + (el.FakeLocation.ToString()));
              }
              
            else if (e.KeyCode == Keys.I)
                hero.Immortal = !hero.Immortal;
            else if (e.KeyCode == Keys.E)
            {
                if(hero.ActualWeapon.BulletId==BulletId.Bomb)
                IsBombLoading = true;
            }
            else if (e.KeyCode == Keys.P)
            {
                if (timer.Enabled)
                    timer.Stop();
                else
                    timer.Start();
            }
            else if (e.KeyCode == Keys.Q)
            {
                hero.ChangeWeapon();
                RefreshBulletBoard();
            }
            else if (e.KeyCode == Keys.M)
            {
                isMessagePressed = true;
                font = GameSettings.Default.MessageFont;
                fontColor = GameSettings.Default.MessageColor;
            }
            else if (e.KeyCode == Keys.N)
            {
                mapLevel++;
                Start();
            }
            else if(e.KeyCode==Keys.Escape)
            {
                esacepeButtonEvent.Invoke();
            }
            if (e.KeyCode == Keys.W || e.KeyCode == Keys.A || e.KeyCode == Keys.S || e.KeyCode == Keys.D)
            {
                
                if (pressedKeys.Count < 2)
                {
                    if (!pressedKeys.Contains(e.KeyCode))
                    {
                        pressedKeys.Add(e.KeyCode);
                    }
                }

            }
            var list = from VAR in drawingList where VAR.Movable==false && VAR.ElementId==ElementId.Obstacle select VAR;
            
            Point point = hero.Move(pressedKeys);
            if(!(point.X-step<=0||point.X+hero.Width+step>=mapWidth||point.Y-2*step<=0||point.Y+hero.Width+step>=mapHeight))
            {
               if(CheckMove(point.X,point.Y,list.ToList())&&CheckMove(point.X+hero.Width,point.Y,list.ToList())&&CheckMove(point.X,point.Y+hero.Height,list.ToList())&&CheckMove(point.X+hero.Width,point.Y+hero.Height,list.ToList()))
                    hero.FakeLocation = point;
            }
            GiveBonus(hero);
            RotateEnemy();
            DisplayScreen();
           
            base.OnKeyDown(e);
        }
        /// <summary>
        /// Obsługa podniesienia klawisza
        /// </summary>
        /// <param name="e">Argument przycisku</param>
        protected override void OnKeyUp(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.E)
            {
               
                bombCount = 0;
                IsBombLoading = false;
            }
            else if(e.KeyCode==Keys.M)
            {
                isMessagePressed = false;
            }
            pressedKeys.Remove(e.KeyCode);
            var list = from VAR in drawingList where VAR.Movable == false && VAR.ElementId == ElementId.Obstacle select VAR;

            Point point = hero.Move(pressedKeys);
            if (!(point.X - step <= 0 || point.X + hero.Width + step >= mapWidth || point.Y - 2 * step <= 0 || point.Y + hero.Width + step >= mapHeight))
            {
                if (CheckMove(point.X, point.Y, list.ToList()) && CheckMove(point.X + hero.Width, point.Y, list.ToList()) && CheckMove(point.X, point.Y + hero.Height, list.ToList()) && CheckMove(point.X + hero.Width, point.Y + hero.Height, list.ToList()))
                    hero.FakeLocation = point;
            }
            GiveBonus(hero);
            RotateEnemy();
            DisplayScreen();
            base.OnKeyUp(e);
        }
        /// <summary>
        /// Obsługa kliknięcia myszki
        /// </summary>
        /// <param name="e">Argument przycisków</param>
        protected override void OnMouseClick(MouseEventArgs e)
        {
            if(timer.Enabled==false)
            {
                timer.Start();
            }
            if(hero==null)
                return;

            RefreshBulletBoard();
            if (hero.ActualWeapon.BulletCount == 0||hero.ActualWeapon.BulletId==BulletId.Bomb)
                return;
            Bullet bullet = hero.Shot(CalculateFakePostions(e.Location, hero));
            if(bullet==null)
                return;
            gameGraphic.SetElementSize(bullet);
            CalculateRealPositions(bullet, hero);
            bullet.CalculateRotation(e.Location);
            bulletList.Add(bullet);
            gameSound.PlaySound(hero.ActualWeapon.SoundId,false);
            base.OnMouseClick(e);
        }
        /// <summary>
        /// Obsługa ruchu myszki
        /// </summary>
        /// <param name="e">Argument przycisków</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if(hero!=null)
            hero.CalculateRotation(e.Location);
            base.OnMouseMove(e);
        }
        /// <summary>
        /// Obsługa rysowania
        /// </summary>
        /// <param name="e">Argument rysowania</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (drawingList != null)
            {
            foreach (Element element in drawingList)
            {
                e.Graphics.DrawImage(gameGraphic.GetImage(element), element.RealLocation);

            }
            if (IsBombLoading)
            {
                DrawRectangle(e.Graphics, ref loadRectangle);
            }
            if(isMessagePressed)
            {
                SetMessage(message,font,e.Graphics);
                e.Graphics.DrawString(message,font,new SolidBrush( fontColor),messageRectangle);
            }
            }

    base.OnPaint(e);
        }
        #endregion 
       

        #region Functions
        /// <summary>
        /// Koniec gry
        /// </summary>
        private void GameOver()
        {
            savePointEvent.Invoke(pts);

            pts = oldPts;
            isMessagePressed = true;
            font = GameSettings.Default.GameOverFont;
            fontColor = GameSettings.Default.GameOverColr;

            message = "GAME OVERR   ";
            OnPaint(new PaintEventArgs(CreateGraphics(),ClientRectangle));
            System.Threading.Thread.Sleep(1000);
            isMessagePressed = false;
            wallList.Clear();
            
            Start();
        }
        /// <summary>
        /// Odswieza panel z informacjami o graczu
        /// </summary>
        private void RefreshBulletBoard()
        {
            if(refreshBoard!=null)
                    refreshBoard.Invoke();

        }
        /// <summary>
        /// Oblicza "prawdziwa" pozycje
        /// </summary>
        /// <param name="element"></param>
        /// <param name="followedElement"></param>
        private void CalculateRealPositions(Element element, Element followedElement)
        {
            element.RealX = element.FakeX < followedElement.FakeX ? (Width / 2 - (followedElement.FakeX - element.FakeX)) : (Width / 2 + (element.FakeX - followedElement.FakeX));
            element.RealY = element.FakeY < followedElement.FakeY ? (Height / 2 - (followedElement.FakeY - element.FakeY)) : (Height / 2 + (element.FakeY - followedElement.FakeY));
            //board.SetElement(element);
        }
        /// <summary>
        /// Sprawdza czy element znajduje sie w ekranie do wyswietlenia
        /// </summary>
        /// <param name="el">Sprawdzany Element</param>
        /// <param name="x">Lewa krawędź</param>
        /// <param name="y">Górna krawędź</param>
        /// <returns></returns>
        private bool InsideScreen(Element el, int x, int y)
        {
            if (x > el.FakeX - Width / 2 && y > el.FakeY - Height / 2 && x < el.FakeX + Width / 2 && y < el.FakeY + Height / 2)
                return true;
            return false;
        }
        /// <summary>
        /// Dodaje listy elementow do drawingList
        /// </summary>
        private void DisplayScreen()
        {
            if (!started)
                return;
            FollowElement(hero, enemyList, drawingList);
            FollowElement(hero, obstacleList, drawingList);
            FollowElement(hero,bombList,drawingList);
            FollowElement(hero,wallList,drawingList);
            FollowElement(hero,aimList,drawingList);
            FollowElement(hero,bonusList,drawingList);
            FollowElement(hero,deadElementList,drawingList);
            FollowElement(hero,animationList,drawingList);
        }
        /// <summary>
        /// Podazanie ekranu za elementem
        /// </summary>
        /// <typeparam name="T">Typ parametru</typeparam>
        /// <param name="followed">Element podązany</param>
        /// <param name="list">Lista elementow</param>
        /// <param name="drawList">List do rysowania</param>
        private void FollowElement<T>(Element followed, List<T> list, List<Element> drawList) where T : Element
        {

            foreach (T el in list)
            {

                if (!(InsideScreen(followed, el.FakeX, el.FakeY) || InsideScreen(followed, el.FakeX, el.FakeY + el.Height)
                || InsideScreen(followed, el.FakeX + Width, el.FakeY) || InsideScreen(followed, el.FakeX + el.Width, el.FakeY + el.Height)))
                {
                    if (drawList.Contains(el))
                        drawList.Remove(el);
                }
                else
                {
                    if (!drawList.Contains(el))
                        drawList.Add(el);
                    CalculateRealPositions(el, hero);
                   
                }

            }
           drawingList.Sort();
        }
        /// <summary>
        /// Wylicza "fake" pozycje na podstawie prawdziwej
        /// </summary>
        /// <param name="point">Punkt</param>
        /// <param name="followedElement">Element podązany</param>
        /// <returns>Zwraca "fake" Point</returns>
        private Point CalculateFakePostions(Point point, Element followedElement)
        {
            Point fakePoint = new Point();
            fakePoint.X = point.X < followedElement.RealX ? (followedElement.FakeX - Width / 2 + point.X) : (point.X - Width / 2 + followedElement.FakeX);
            fakePoint.Y = point.Y < followedElement.RealY ? (followedElement.FakeY - Height / 2 + point.Y) : (point.Y - Height / 2 + followedElement.FakeY);

            return fakePoint;
        }
        /// <summary>
        /// Sprawdza czy nie weszło się w element
        /// </summary>
        /// <param name="x">Lewy róg</param>
        /// <param name="y">Górny róg</param>
        /// <param name="list"></param>
        /// <returns></returns>
        private bool CheckMove(int x, int y, List<Element> list)
        {
            foreach (Element element in list)
            {
                if (!element.Movable)
                {
                    if (element.InsideElement(x, y))
                        return false;

                }
            }
            return true;
        }
        /// <summary>
        /// Sprawdza czy nie ma przeszkód do oddania strzału
        /// </summary>
        /// <param name="bullet">Pocisk</param>
        /// <param name="list">lista z przeszkodami</param>
        /// <param name="owner">Właściciel pocisku</param>
        /// <returns></returns>
        private bool CheckShoot(Bullet bullet, List<Element> list, ElementId owner)
        {
            int x = bullet.FakeX;
            int y = bullet.FakeY;
            foreach (Element element in list)
            {
                if (owner == element.ElementId)
                    continue;
                if (!element.Shootable)
                {
                    if (element.InsideElement(x, y)||element.InsideElement(x+bullet.Width,y)||element.InsideElement(x,y+bullet.Height)||element.InsideElement(x+bullet.Width,y+bullet.Height))
                    {
                        if(!element.GetDemage(bullet.Demage))
                        {
                            element.IsLive = false;
                            deadList.Add(element);
                            
                            if (bullet.Owner == ElementId.Hero)
                            {
                                hero.Pts += element.Points;
                                pts += element.Points;
                                RefreshBulletBoard();    
                            }
                            else if(element.ElementId==ElementId.Hero)
                            {
                                   GameOver();
                            }
                            
                        }
                        if(element.ElementId==ElementId.Hero)
                            RefreshBulletBoard();
                        return false;
                    }
                }
            }
            return true;
        }
        private double Distance(Point point1,Point point2)
        {
            return Math.Sqrt(Math.Pow(point1.X - point2.X,2) +(Math.Pow(point1.Y - point2.Y,2)));
        }
        /// <summary>
        /// Znajduje najkrótszą ścieżkę do elementu
        /// </summary>
        /// <param name="e1">Element szukany</param>
        /// <param name="e2">Element szukający</param>
        private void FindElement(Element e1, Element e2)
        {
            board.SetElement(e2);
            board.SetElement(e1);
            e2.Path = board.Astar(e1.BoardLocation, e2.BoardLocation, e2);
            e2.MoveCount = 0;
        }
        /// <summary>
        /// Usuwa element  z list
        /// </summary>
        /// <typeparam name="T">Dziedziczy po klasie Element</typeparam>
        /// <param name="element">Usuwany element</param>
        /// <param name="list">List z której element będzie usuwany</param>
        /// <returns></returns>
        private bool DeleteFromList<T>(Element element, List<T> list) where T : Element
        {
            if (!list.Contains(element))
                return false;
            if (drawingList.Contains(element))
                drawingList.Remove(element);
            list.Remove(element as T);

            gameSound.PlaySound(SoundID.Beep1,false);
            return true;
        }
        /// <summary>
        /// Ustawia wiadomość do wyswietlania
        /// </summary>
        /// <param name="str">Wiadomosc</param>
        /// <param name="f">Czcionka</param>
        /// <param name="graphics">Graphics</param>
        private void SetMessage(string str,Font f,Graphics graphics)
        {
            messageRectangle.Width=(int)(graphics.MeasureString(str,f)).Width;
            messageRectangle.Height = (int)(graphics.MeasureString(str, f)).Height;

            messageRectangle.X = Width/2 - messageRectangle.Width/2;
            messageRectangle.Y = Height/2 - messageRectangle.Height;

        }
        /// <summary>
        /// Rysuje prostokąt postępu
        /// </summary>
        /// <param name="graphics">Graphics</param>
        /// <param name="rectangle">Prostokąt</param>
        /// <returns></returns>
        private void DrawRectangle(Graphics graphics,ref Rectangle rectangle)
        {
           
            rectangle.Width = (loadRecWidth*bombCount/100);//
            
            graphics.FillRectangle(Brushes.DarkRed,rectangle);
                
        }
        /// <summary>
        /// Sprawdza tor pocisku wystrzelonego przez przeciwnika
        /// </summary>
        /// <param name="enemy">Strzelający przeciwnik</param>
        /// <returns></returns>
        private bool CheckBulletTrack(Enemy enemy)
        {
            Point aimPoint=new Point();
            aimPoint.X = hero.FakeX + hero.Width/2;
            aimPoint.Y = hero.FakeY + hero.Height/2;
             var list = from VAR in drawingList where VAR.Shootable == false && VAR.ElementId==ElementId.Obstacle select  VAR;
            Bullet bullet = enemy.CheckShoot((aimPoint));
            if (bullet == null)
                return false;
            gameGraphic.SetElementSize(bullet);
            CalculateRealPositions(bullet,enemy);
            bullet.CalculateRotation(enemy.RealLocation);
            
            while (bullet.Move())
            {
                foreach (var element in (list))
                {
                    if (element.InsideElement(bullet))
                        return false;
                }

                if (hero.InsideElement(bullet))
                    return true;
        
            }
            return false;
        }
        /// <summary>
        /// Tworzy explozje niszcząc obiekty znajdujące sie w liście 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bomb">Pocisk tworzący explozje</param>
        /// <param name="list">Lista obiektów</param>
        private void MakeExplosion<T>(Bullet bomb,List<T> list) where T : Element
        {
            for(int i=0;i<list.Count;i++)
            {
                Element element = list[i];
                if(element.Immortal)
                    continue;
                if(bomb.MakeExplosion(element))
                {
                    if (drawingList.Contains(element))
                        drawingList.Remove(element);

                    pts += element.Points;
                    hero.Pts += element.Points;
                    deadList.Add(element);
                    RefreshBulletBoard();
                  //  i = i - 1;
                }
            }
        }
        /// <summary>
        /// Daje bonus
        /// </summary>
        /// <param name="character">Postać która ma otrzymać bonus</param>
        private void GiveBonus(Character character)
        {
            var bonuses = from VAR in bonusList where drawingList.Contains(VAR) select  VAR;
            for (int i = 0; i < bonuses.Count();i++ )
            {
                Bonus bonus = (bonuses.ToList())[i];
                if (character.InsideElement(bonus.FakeX + bonus.Width / 2, bonus.FakeY + bonus.Height / 2))
                {
                    bonus.GiveBonus(character);
                    DeleteFromList(bonus, bonusList);
                    RefreshBulletBoard();
                    i = i - 1;
                }
            }

        }
        /// <summary>
        /// Zwraca DeadId
        /// </summary>
        /// <param name="str">Rodzaj obiektu</param>
        /// <returns></returns>
        private DeadId GetDeadId(string str)
        {
            Array list = (Enum.GetValues(typeof(DeadId)));
            foreach(DeadId s in list)
            {
                if (str == s.ToString())
                    return s;
            }
            return DeadId.EnemyId;
        }
        /// <summary>
        /// Odpauzowuje gre
        /// </summary>
        public void Resume()
        {
            if(timer.Enabled==false)
            timer.Start();
            stoped = false;
        }
        /// <summary>
        /// Zatrzymuje gre
        /// </summary>
        public void Stop()
        {
            if (timer.Enabled == true)
                timer.Stop();
            stoped = true;
        }

        #endregion
        #region TimerActions
        /// <summary>
        /// Obrót przeciwników
        /// </summary>
        private void RotateEnemy()
        {
            var drawingEnemies = from VAR in drawingList where VAR.ElementId == ElementId.Enemy select VAR;
            foreach (Enemy e in drawingEnemies)
            {
                e.CalculateRotation(hero.RealLocation);
            }
        }
        /// <summary>
        /// Odswieżanie ekranu
        /// </summary>
        private void RefreshScreen()
        {
            Refresh();

        }
        /// <summary>
        /// Ruch pocisku
        /// </summary>
        private void BulletMove()
        {
            var list = from VAR in drawingList where VAR.Shootable == false && VAR.ElementId != ElementId.Bullet select VAR;
            for (int i = 0; i < bulletList.Count; i++)
            {
                Bullet bullet = bulletList[i];
                Point point = bullet.FakeLocation;
                if (!bullet.Move() ||
                 (!(CheckShoot(bullet, list.ToList(), bullet.Owner) && CheckShoot(bullet, list.ToList(), bullet.Owner) &&
                 CheckShoot(bullet, list.ToList(), bullet.Owner) && CheckShoot(bullet, list.ToList(), bullet.Owner))))
                {
                    drawingList.Remove(bullet);
                    bulletList.Remove(bullet);
                    i = i - 1;
                }

                FollowElement(hero, bulletList, drawingList);
            }

       
        }
        /// <summary>
        /// Szukanie bohatera przez przeciwników
        /// </summary>
        private void EnemyFind()
        {
            var list = from VAR in drawingList where VAR.ElementId == ElementId.Enemy select VAR;
            while (true)
            {

                if (findCount >= enemyList.Count)
                {
                    findCount = 0;
                    return;
                }
                if(!list.Contains(enemyList[findCount]))
                {
                    findCount++;
                    continue;
                    
                }
                if (drawingList.Contains(enemyList[findCount])&&enemyList[findCount].EnemyId==EnemyId.Droideka)
                {
                    board.SetElement(enemyList[findCount]);
                    FindElement(hero, enemyList[findCount]);
                    findCount = (findCount + 1) % enemyList.Count;
                    return;
                }
                else if (drawingList.Contains(enemyList[findCount]) && enemyList[findCount].EnemyId == EnemyId.DroidB2&&enemyList[findCount].IsAwake)
                {
                    board.SetElement(enemyList[findCount]);
                    FindElement(hero, enemyList[findCount]);
                    findCount = (findCount + 1) % enemyList.Count;
                    return;
                }
                findCount++;
            }
        }
        /// <summary>
        /// Ruch przeciwników
        /// </summary>
        private void EnemyMove()
        {
            var list = from VAR in drawingList where VAR.ElementId == ElementId.Enemy select VAR;
            var listObstacle = from VAR in drawingList where VAR.Movable == false && VAR.ElementId == ElementId.Obstacle select VAR;
            foreach (Element element in list)
            {
                Enemy enemy = element as Enemy;
                Point p = element.FakeLocation;
                if (enemy.Path != null && enemy.MoveCount < enemy.Path.Count&&enemy.EnemyId!=EnemyId.DroidB1)
                {
                    enemy.FakeLocation = element.Path[element.MoveCount];
                    enemy.MoveCount++;
                }
                else if(enemy.EnemyId==EnemyId.DroidB1&&!CheckBulletTrack(enemy))
                {
                    Point point=new Point();
                    point.X = enemy.FakeX + enemy.DirectedVerticalStep*step;
                    point.Y = enemy.FakeY + enemy.DirectedHorizontalStep*step;
                    if ((CheckMove(p.X, p.Y, listObstacle.ToList()) && CheckMove(p.X+enemy.Width, p.Y, listObstacle.ToList()) && 
                        CheckMove(p.X, p.Y+enemy.Height, listObstacle.ToList()) && CheckMove(p.X+enemy.Width, p.Y+enemy.Height, listObstacle.ToList())))
                        enemy.FakeLocation = point;
                }

            }
            FollowElement(hero, enemyList, drawingList);
        }
        /// <summary>
        /// Usuwa zniszczone elementy
        /// </summary>
        private void DeleteDeadElement()
        {

            for (int i = 0; i < deadList.Count; i++)
            {
                Element element = deadList[i];
                
                switch (element.ElementId)
                {
                    case ElementId.Enemy:
                        {
                            DeadElement d=   new DeadElement(step,difficulty,DeadId.EnemyId);
                            gameGraphic.SetElementSize(d);

                            gameGraphic.SetElementSize(d);
                            d.FakeLocation = element.FakeLocation;
                            deadElementList.Add(d);
                            
                            DeleteFromList(element, enemyList);
                            break;
                        }
                    case ElementId.Obstacle:
                        {
                            if(element.Kind=="Tree")
                            {
                                DeadElement d = new DeadElement(step, difficulty, DeadId.Tree);
                                gameGraphic.SetElementSize(d);

                                gameGraphic.SetElementSize(d);
                                d.FakeLocation = element.FakeLocation;
                                deadElementList.Add(d);

                            }
                            DeleteFromList(element, obstacleList);
                            DeleteFromList(element, aimList);
                            break;
                        }
                    case ElementId.Hero:
                        {
                            GameOver();
                            break;
                        }

                }

                board.SetElement(element);
                board.SetField(element, false);
               
                
                deadList.Remove(element);
                i = i - 1;
                
        }
        DisplayScreen();
    }
        /// <summary>
        /// Ladowanie paska postępu bomby
        /// </summary>
        private void BombLoad()
        {
            if(bombCount>100)
            {
                Bullet bomb = hero.PlantBomb();
                if(bomb==null)
                    return;
                bomb.FakeLocation = hero.FakeLocation;

                gameGraphic.SetElementSize(bomb);
                CalculateRealPositions(bomb, hero);
                bomb.CalculateRotation(hero.RealLocation);
                bombList.Add(bomb);
                bombCount = 0;
                RefreshBulletBoard();
            }
            bombCount += 5;
        }
        /// <summary>
        /// Explozja bomby
        /// </summary>
        private void ExplodeBomb()
        {
            for(int i=0;i<bombList.Count;i++)
            {
                Bullet bomb = bombList[i];
                bomb.ActualTimerCount++;
                if(bomb.ActualTimerCount==bomb.TimerCount)
                {
                    MakeExplosion(bomb,enemyList);
                    MakeExplosion(bomb,obstacleList);
                    MakeExplosion(bomb,aimList);
                    MakeExplosion(bomb,new List<Hero>{hero});
                    
                    if (drawingList.Contains(bomb))
                        drawingList.Remove(bomb);
                    bombList.Remove(bomb);
                    i = i - 1;
                    bomb.IsAnimated = true;
                    animationList.Add(bomb);
                    gameSound.PlaySound(SoundID.Explosion,false);
                }
            }
        }
        /// <summary>
        /// Sprawdza czy wykonany jest cel misjii
        /// </summary>
        private void CheckAimMission()
        {
            if(hero.Life<=0)
                GameOver();
            if(aimList.Count==0)
            {

                oldPts = pts;
                if(mapLevel==5)
                {
                    savePointEvent.Invoke(pts);
                    font = GameSettings.Default.VictoryFont;
                    fontColor = GameSettings.Default.VictoryColor;
                    message = "VICTORY !!!";
                    isMessagePressed = true;
                    savePointEvent.Invoke(pts);
                    pts = 0;
                    Refresh();
                    System.Threading.Thread.Sleep(4000);
                    LoadMission(1);

                }
                else
                {
                    mapLevel++;
                    Start();
                }
            }
 
        }
        /// <summary>
        /// Atak przeciwników
        /// </summary>
        private void EnemyAtack()
        {
            Point point=new Point();
            point.X = hero.FakeLocation.X + hero.Width/2;
            point.Y = hero.FakeY + hero.Height/2;
            var list = from VAR in drawingList where VAR.ElementId == ElementId.Enemy  select VAR;
            foreach (Enemy enemy in list)
            {
                Bullet bullet;
                if(!CheckBulletTrack(enemy))
                    continue;
                
                if((bullet=enemy.Shoot(point))!=null)
                {
                    gameGraphic.SetElementSize(bullet);
                    CalculateRealPositions(bullet, hero);
                    bullet.CalculateRotation(hero.RealLocation);
                    bulletList.Add(bullet);
                    gameSound.PlaySound(enemy.ActualWeapon.SoundId,false);
                }
            }
        }
        /// <summary>
        /// Budzie przeciwników 
        /// </summary>
        private void EnemyWakeUp()
        {
            Point point=new Point();
            point.X = hero.FakeX + hero.Width/2;
            point.Y = hero.FakeY + hero.Height/2;
            var list = from VAR in drawingList where VAR.ElementId == ElementId.Enemy && VAR.IsAwake==false select VAR;
            foreach (Element element in list)
            {
                Enemy enemy = element as Enemy;
                if(CheckBulletTrack(enemy))
                {
                    enemy.IsAwake = true;
                }
            }

        }
        /// <summary>
        /// Zmienia kierunek poruszania się drodiów B1
        /// </summary>
        private void DroidDirection()
        {
            Random rnd=new Random();
            var list = from VAR in enemyList where VAR.EnemyId==EnemyId.DroidB1 select VAR;
            foreach (var enemy1 in list)
            {
                enemy1.DirectedHorizontalStep = 1 - rnd.Next(2);
                
                enemy1.DirectedVerticalStep = 1 - rnd.Next(2);
                if(enemy1.DirectedHorizontalStep==0&&enemy1.DirectedVerticalStep==0)
                {
                    while ((enemy1.DirectedVerticalStep = 1 - rnd.Next(2)) != 0) ;
                }
            }
        }
        /// <summary>
        /// Usuwa ciała, śmieci po czasie
        /// </summary>
        private void RemoveDeadElements()
        {
            for(int i=0;i<deadElementList.Count;i++)
            {
                DeadElement dead = deadElementList[i];
                
                dead.ActualTimerCount++;
                if(dead.ActualTimerCount==dead.TimerCount)
                {
                    deadElementList.Remove((dead));
                    if (drawingList.Contains(dead))
                        drawingList.Remove(dead);
                    i = i - 1;
                    
                }
            }
        }
        /// <summary>
        /// Animacja 
        /// </summary>
        private void MakeAnimation()
        {
            for(int i=0;i<animationList.Count;i++)
            {
                Element element = animationList[i];

                if(element.AnimationCount<gameGraphic.GetAnimationCount(element)-1)
                {
                    element.AnimationCount++;    
                }
                else
                {
                    if (drawingList.Contains(element))
                        drawingList.Remove(element);

                    animationList.Remove(element);
                    i = i - 1;
                }

            }
        }

        #endregion

        
    }
}